package com.ruoyi.youtian.threadConfig;


import com.ruoyi.common.config.thread.AbstractWork;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.gzmx_tag_history.domain.Gzmx_tag_history;
import com.ruoyi.gzmx_tag_history.service.IGzmx_tag_historyService;
import com.ruoyi.youtian.domain.*;
import com.ruoyi.youtian.domain.vo.ScenePointVo;
import com.ruoyi.youtian.domain.vo.TEarlyWarningVo;
import com.ruoyi.youtian.domain.vo.WarningAlgorithm;
import com.ruoyi.youtian.mapper.TEarlyWarningMapper;
import com.ruoyi.youtian.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 预警异步分析
 */
@Slf4j
public class AsyncAnalyseEarlyWarning extends AbstractWork {

    /**
     * 预警值进行比较
     */
    private ITEarlyWarningService itEarlyWarningService;

    /**
     * 点位历史记录值
     */
    private IGzmx_tag_historyService gzmx_tag_historyService;


    private ITEarlyWarningRecordService itEarlyWarningRecordService;

    private ITEarlyWarningRecordDetailService itEarlyWarningRecordDetailService;

    private IFaultRecordService faultRecordService;


    private TEarlyWarningVo tEarlyWarning;

    @Autowired
    private TEarlyWarningMapper tEarlyWarningMapper;
    /**
     * 场景id
     */
    private Long sceneId;

    private Map<Long, TEarlyWarning> faultNodeMap = new HashMap<>();




    public AsyncAnalyseEarlyWarning(String workDescription,ITEarlyWarningService itEarlyWarningService, IGzmx_tag_historyService gzmx_tag_historyService, ITEarlyWarningRecordService itEarlyWarningRecordService,
                                    ITEarlyWarningRecordDetailService itEarlyWarningRecordDetailService ,    IFaultRecordService faultRecordService,  TEarlyWarningVo tEarlyWarning) {
        super(workDescription);
        this.itEarlyWarningRecordDetailService = itEarlyWarningRecordDetailService;
        this.gzmx_tag_historyService = gzmx_tag_historyService;
        this.itEarlyWarningRecordService = itEarlyWarningRecordService;
        this.itEarlyWarningService = itEarlyWarningService;
        this.faultRecordService = faultRecordService;
        this.tEarlyWarning = tEarlyWarning;
    }

    /**
     * 预警设置异常算法分析
     * 4中算法、0 1为同比环比 2为离散 3为其他
     */
    @Override
    public void process() {
        //查询预警列表基本数据
        List<TEarlyWarning> list = tEarlyWarningMapper.selectTEarlyWarningListtrs();
        if (list != null) {
            for (TEarlyWarning warning : list) {
                WarningAlgorithm algorithm = new WarningAlgorithm();
                BeanUtils.copyProperties(warning,algorithm);
                //判断每一条预警设置的状态是启用或禁用，0 为启用 1为禁用 如为禁用直接结束本次循环并执行下一次的循环
                if (Integer.parseInt(warning.getStatus()) == 0) {
                    //根据类型选择对应的算法分析执行流程
                    switch (Integer.parseInt(warning.getWarningType())){
                        case 0:
                            //同比环比都封装到一个方法中
                            yoymethod();
                            break;
                        case 1:
                            //同比环比都封装到一个方法中
                            yoymethod();
                            break;
                        case 2:
                            //如果点位id和点位名称为null，证明这条预警设置选择的是场景下的所有点位
                            if (warning.getSourceTag() == null && warning.getDescription() == null) {
                                List<ScenePointVo> scenePointVos = tEarlyWarningMapper.selectscenenByid(warning.getSceneId());
                                for (ScenePointVo scenePointVo : scenePointVos) {
                                    //查询历史数据 并查当前时间往前多少天的数据
                                    LinkedList<Map<String, Object>> mapList = tEarlyWarningMapper.selecthistoryByid(scenePointVo.getPointId(),warning.getDay());
                                    //获取连续日期并存入查出的数据中
                                    List<Map<String, Object>> datesuan = datesuan(warning.getDay());
                                    //把数据导入到list map中
                                    mapList.addAll(datesuan);
                                    //整合完数据调用离散算法 方法
                                    for (Map<String, Object> stringObjectMap : mapList) {
                                        doCal(Double.parseDouble((String) stringObjectMap.get("shangxian")),Double.parseDouble((String) stringObjectMap.get("xiaxian")),mapList,algorithm);
                                    }
                                }
                            }
                            //否则就是证明这条预警设置选择的某一个点位进行的预警设置
                            //查询历史数据 并查当前时间往前多少天的数据
                            LinkedList<Map<String, Object>> maps = tEarlyWarningMapper.selecthistoryByid(warning.getSourceTag(),warning.getDay());
                            //获取连续日期并存入查出的数据中
                            List<Map<String, Object>> datesuan = datesuan(warning.getDay());
                            maps.addAll(datesuan);
                            //单个点位的数据调用离散方法执行
                            for (Map<String, Object> stringObjectMap : maps) {
                                doCal(Double.parseDouble((String) stringObjectMap.get("shangxian")),Double.parseDouble((String) stringObjectMap.get("xiaxian")),maps,algorithm);
                            }
                            break;
                        case 3:
                            //第四种算法还暂定  定下来直接放到这里封装方法执行就可以
                            break;
                    }
                }else {
                    continue;
                }
            }
        }
    }

    /**
     * 计算以当前日期往前多少天的日期推算
     * @param day 天数
     */
    private List<Map<String,Object>> datesuan(Integer day) {
        List<Map<String,Object>> list = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());
        ca.add(Calendar.DATE,day);
        String format = sdf.format(ca.getTime());
        List<String> twoDaysDay = getTwoDaysDay(sdf.format(new Date()), format);
        for (String date : twoDaysDay) {
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("dt",date);
            list.add(hashMap);
        }
//        HashMap<String, Object> map = new HashMap<>();
//        map.put("shijian", format);
//        list.add(map);
        return list;
    }

    /**
     * 获取连续日期
     * @param dateStart 开始日期，也就是当前时间
     * @param dateEnd 结束时间  通过上面推算到day天数到具体时间
     * @return 连续日期集合
     */
    public static List<String> getTwoDaysDay(String dateStart, String dateEnd) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        List<String> dateList = new ArrayList<String>();
        try{
            Date dateOne = sdf.parse(dateStart);
            Date dateTwo = sdf.parse(dateEnd);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(dateTwo);
            dateList.add(dateEnd);
            while(calendar.getTime().after(dateOne)){
                calendar.add(Calendar.DAY_OF_MONTH, -1);
                dateList.add(sdf.format(calendar.getTime()));
            }
        } catch(Exception e){
            e.printStackTrace();
        }
        return dateList;
    }

    /**
     * 执行一个模型的错误分析流程
     * 1.查询模板对应的详情及 目标节点 上下限
     * 2. 执行异常分析
     * 3.执行离散算法
     * 4.保存异常分析快照
     */
    public void yoymethod(){

        // 1. 该场景下的所有点位列表
        ScenePoint scenePoint = new ScenePoint();
        scenePoint.setSceneId(tEarlyWarning.getSceneId());
        //1.2 保存预警主表信息
        WarningAlgorithm algorithm = new WarningAlgorithm();
        /**
         *  查询实时数据
         */
        List<FaultRecord> faultRecordList = faultRecordService.selectFaultRecordList(null);
        if(faultRecordList != null){
            // 1.3 查询历史数据
            String beginTime =  DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD_HH_MM_SS, tEarlyWarning.getStartTime());
            String endTime = DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD_HH_MM_SS, tEarlyWarning.getEndTime());

            HashMap<String, Object> failureRate = calFailureRate(tEarlyWarning.getSceneId(), tEarlyWarning.getStartTime(), tEarlyWarning.getEndTime(), faultRecordList);
            double suan = (double) failureRate.get("suan");
            //  推算历史数据
            Map<String, Date> stringDateMap = DateUtils.calculationDate(beginTime, endTime, Integer.parseInt(tEarlyWarning.getWarningType()));
            Date beginTime2 =  stringDateMap.get("begintime");
            Date endTime2 = stringDateMap.get("endtime");

            HashMap<String, Object> failureRate2 = calFailureRate(tEarlyWarning.getSceneId(), beginTime2, endTime2, faultRecordList);
            double suan2 = (double) failureRate2.get("suan");
            double failureRrowthRate = suan2 - suan;

            if(failureRrowthRate < 0){
                failureRrowthRate = 0;
            }

            if(suan > 1){
                suan = 1;
            }else if(suan < 0){
                suan = 0;
            }

            if(suan2 > 0.20){ // 超过0.20 触发报警
                TEarlyWarningRecord record = new TEarlyWarningRecord();
                record.setAlarmValue(suan2+"");
                record.setWarningType(tEarlyWarning.getWarningType());
                record.setSceneId(tEarlyWarning.getSceneId());
                record.setWarningTime(DateUtils.parseDate(beginTime));
                record.setWarningCode(tEarlyWarning.getWarningCode());
                int i = itEarlyWarningRecordService.insertTEarlyWarningRecord(record);
                algorithm.setScene_id(record.getSceneId());
                algorithm.setAlarm_value(suan2+"");
                algorithm.setWarning_type(Integer.parseInt(tEarlyWarning.getWarningType()));
                algorithm.setWarningTime(DateUtils.parseDate(beginTime));
                algorithm.setSourceTag(failureRate.get("descid").toString());
                algorithm.setWarningRemark(tEarlyWarning.getRemark());
                Date date = new Date();
                SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
                algorithm.setCreate_time(dateFormat.format(date));
                save(algorithm);
                log.info("保存同比环比算法主表信息");
            }


            TEarlyWarningRecordDetail tEarlyWarningRecordDetail = new TEarlyWarningRecordDetail();
            tEarlyWarningRecordDetail.setWarningName(tEarlyWarning.getWarningName());
            tEarlyWarningRecordDetail.setWarningCode(tEarlyWarning.getWarningCode());
            tEarlyWarningRecordDetail.setWarningRemark(tEarlyWarning.getRemark());
            tEarlyWarningRecordDetail.setWarningType(tEarlyWarning.getWarningType());
            tEarlyWarningRecordDetail.setSceneId(tEarlyWarning.getSceneId());
            tEarlyWarningRecordDetail.setFailureRate(new BigDecimal(suan));
            tEarlyWarningRecordDetail.setFailureGrowthRate(new BigDecimal(failureRrowthRate));
            itEarlyWarningRecordDetailService.insertTEarlyWarningRecordDetail(tEarlyWarningRecordDetail);
            WarningAlgorithm warningAlgorithm = new WarningAlgorithm();
            warningAlgorithm.setAlgorithm_id(algorithm.getAlgorithm_id());
            warningAlgorithm.setSourceTag(failureRate.get("descid").toString());
            warningAlgorithm.setScene_id(tEarlyWarning.getSceneId());
            warningAlgorithm.setAlarm(true);
            warningAlgorithm.setWarningTime(DateUtils.parseDate(beginTime));
            warningAlgorithm.setFailureRate(new BigDecimal(suan).toString());
            warningAlgorithm.setFailureGrowthRate(new BigDecimal(failureRrowthRate).toString());
            warningAlgorithm.setWarningRemark(tEarlyWarning.getRemark());
            save(warningAlgorithm);
            log.info("保存同比环比算法子表信息");
            log.info("开始执行离散算法分析");
        }
    }

    private HashMap<String, Object> calFailureRate(Long sceneId, Date begintime, Date endtime , List<FaultRecord> faultRecordList){
        String beginTime =  DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD_HH_MM_SS,this.tEarlyWarning.getStartTime());
        String endTime = DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD_HH_MM_SS,this.tEarlyWarning.getEndTime());
        HashMap<String, Object> map = new HashMap<>();
        List<Gzmx_tag_history> gzmx_tag_historyList = gzmx_tag_historyService.selectGzmx_tagListHistoryBySceneId(sceneId, beginTime, endTime);
        double  res = 0;
        double  size = gzmx_tag_historyList.size();
        for (Gzmx_tag_history gzmxTagHistory : gzmx_tag_historyList) {
            for(FaultRecord faultRecord : faultRecordList){
                if(faultRecord.getFaultDesc().contains(gzmxTagHistory.getSourceTag())){
                    res ++;
                    continue;
                }
            }
            map.put("descid",gzmxTagHistory.getSourceTag());
        }

        double failureRate = res/size;
        if(Math.abs(failureRate) < 0.000000000001){
            failureRate = 0.0;
        }
        map.put("suan",failureRate);
        return map;
    }




//    @Autowired
//    private DispersedMapper dispersedMapper;

    /**
     * 预警分析——离散算法
     * @param shangxian 监测点位的上限值
     * @param xiaxian 监测点位的下限值
     * @param lishi 监测点位的采集历史数据值
     *              格式 [
     *                      {dt: 2022-01-11 22:30:00, value: 14},
     *                      {dt: 2022-01-11 22:31:00, value: 15}
     *                      ...
     *                  ]
     * @return
     */
    public void doCal(double shangxian, double xiaxian, LinkedList<Map<String, Object>> lishi, WarningAlgorithm warningAlgorithm){
        log.info("离散算法开始执行");
        // 键值名称
        String datetimekey = "dt";
        String valuekey = "valuie";
        String lskey = "lisan";
        String isalartmkey = "alarm";


        /**************1、总和/个数***********/
        //历史数据总和
        double zongshu = 0;
        for (Map<String, Object> map : lishi) {
            zongshu += Double.parseDouble(map.get(valuekey).toString());
        }
        log.info("历史数据总和为{}",zongshu);

        /**************2、计算总数平均值***********/
        //历史数据的平均值
        double lishipiingjun = 0;
        //离散值数据保存
        ArrayList<Object> list = new ArrayList<>();
        lishipiingjun = zongshu/lishi.size();
        log.info("历史平均值{}",lishipiingjun);

        /**************3、平均值与历史数据差值（历史离散值）***********/
        double var ;
        //历史数据减去平均值
        for (Map<String, Object> obj : lishi) {
            obj.put(lskey, Double.parseDouble(obj.get(valuekey).toString()) - lishipiingjun);
        }

        /**************4、上下限平均值***********/
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(4);
        //上下限平均值
        double pingjun = 0;
        pingjun = (shangxian+xiaxian)/2;
        log.info("平均值{}",nf.format(pingjun));
        /**************5、上下限平均值与上下限的差值（上下限离散值）***********/
        double zheng = 0.0;
        double fu = 0.0;
        zheng = Math.abs(pingjun-xiaxian);
        fu = -1*zheng;

        /**************6、判断历史离散值与上下限离散值的比对***********/
        boolean isalarm = false;
        for(Map<String, Object> obj : lishi){
            //不正常的点位id存到固定的表中
            //展示时只展示为正常的点位id并去查询详细信息
            log.info("历史离散值{}",obj.get(lskey).toString());
            if (Double.parseDouble(obj.get(lskey).toString())<fu || Double.parseDouble(obj.get(lskey).toString())>zheng) {
                isalarm = true;
                obj.put(isalartmkey, true);
            }else{
                obj.put(isalartmkey, false);
            }
            if(isalarm){
                //插入数据库   lishi插入详情表   并且  主表插入一条该监测点的告警信息
                log.info("插入主表监测点");
                Date date = new Date();
                SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
                warningAlgorithm.setDispersed_value(lishi.get(0).get("lisan").toString());
                warningAlgorithm.setCreate_time(dateFormat.format(date));
                warningAlgorithm.setWarning_type(2);
//                save();
                log.info("插入字表监测点详情表");

            }
        }
    }


    /**
     * 离散算法公用保存方法
     * @param warningAlgorithm 算法保存对象
     */
    public void save(WarningAlgorithm warningAlgorithm){
        if (warningAlgorithm != null) {
            log.info("执行数据保存操作，保存的数据{}",warningAlgorithm);
//            int i = dispersedMapper.savedispersedlists(warningAlgorithm);
//            if (i > 0){
//                log.info("保存数据成功");
//            }
        }
    }

}
